package cn.edu.sdjzu.xg.eagle.controller;

import cn.edu.sdjzu.xg.eagle.entity.User;
import cn.edu.sdjzu.xg.eagle.exception.AuthenticationException;
import cn.edu.sdjzu.xg.eagle.exception.BysjException;
import cn.edu.sdjzu.xg.eagle.factory.ServiceFactory;
import cn.edu.sdjzu.xg.eagle.service.UserServiceInterface;
import cn.edu.sdjzu.xg.eagle.util.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.Date;
import java.sql.SQLException;

@WebServlet("/login.ctl")
public class LoginCtrl extends HttpServlet {
    private static final Logger logger = LoggerFactory.getLogger(LoginCtrl.class);
    private static final String SECRET_KEY = "XG23";
    private static final long TOKEN_EXPIRATION_TIME = 30 * 60 * 1000; // 30分钟

    private final UserServiceInterface userService = ServiceFactory.getService(
            UserServiceInterface.class,
            "UserServiceImpl"
    );

    private final JwtTokenGenerator jwtTokenGenerator = new JwtTokenGenerator(SECRET_KEY, TOKEN_EXPIRATION_TIME);
    private final ResponseHandler responseHandler = new ResponseHandler();

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) {
        response.setContentType("application/json;charset=UTF-8");

        try {
            //根据请求中的用户名和密码构建没有id的User对象
            User userToLogin = parseUserFromRequest(request);
            //验证用户合法性，如果合法，则userToLogin获得id
            validateUserCredentials(userToLogin);
            //根据用户id构建令牌
            String token = jwtTokenGenerator.generateToken(userToLogin.getId());
            //根据令牌构建作出响应
            responseHandler.sendSuccessResponse(response, token);
        } catch (IllegalArgumentException e) {
            logger.warn("无效请求参数: {}", e.getMessage());
            responseHandler.sendErrorResponse(response, e.getMessage(), 400);
        } catch (AuthenticationException e) {
            logger.warn("认证失败: {}", e.getMessage());
            responseHandler.sendErrorResponse(response, e.getMessage(), 401);
        } catch (SQLException e) {
            logger.error("数据库操作异常", e);
            responseHandler.sendErrorResponse(response, "数据库操作异常", 500);
        } catch (Exception e) {
            logger.error("系统异常", e);
            responseHandler.sendErrorResponse(response, "系统异常", 500);
        }
    }

    private User parseUserFromRequest(HttpServletRequest request) throws IOException {
        String userJson = JSONUtil.getJSON(request);
        User user = JSON.parseObject(userJson, User.class);

        if (user == null || user.getUsername() == null || user.getPassword() == null) {
            throw new IllegalArgumentException("请求参数不完整");
        }
        return user;
    }


    private void validateUserCredentials(User user) throws SQLException, AuthenticationException {
        userService.login(user);
        if (user.getId() == null) {
            throw new BysjException("用户不存在/密码错误");
        }
    }

    /**
     * JWT Token 生成器。
     * 这里使用静态内部类，如果使用非静态内部类，则有以下劣势：
     * 1.每个 JwtTokenGenerator 实例都会持有外部 LoginCtrl 实例的引用，而Servlet 是长生命周期对象，可能导致内存无法及时释放
     * 2.如果外部类（Servlet）被序列化，内部类会连带被序列化，可能暴露敏感信息（如 secretKey）
     * 3. 不必要的耦合：内部类可以访问外部类的所有成员（包括private），但实际上 token 生成器只需要 secretKey 和 expirationTime
     */
    private static class JwtTokenGenerator {
        private final String secretKey;
        private final long expirationTime;

        public JwtTokenGenerator(String secretKey, long expirationTime) {
            this.secretKey = secretKey;
            this.expirationTime = expirationTime;
        }

        public String generateToken(Long userId) {
            return JWT.create()
                    .withSubject(String.valueOf(userId))
                    .withExpiresAt(new Date(System.currentTimeMillis() + expirationTime))
                    .sign(Algorithm.HMAC256(secretKey));
        }
    }

    /**
     * 响应处理器
     */
    private static class ResponseHandler {
        public void sendSuccessResponse(HttpServletResponse response, String token) throws IOException {
            JSONObject responseBody = new JSONObject();
            responseBody.put("message", "登录成功");
            responseBody.put("code", 200);
            responseBody.put("token", token);
            sendResponse(response, responseBody);
        }

        public void sendErrorResponse(HttpServletResponse response, String errorMsg, int statusCode) {
            try {
                JSONObject responseBody = new JSONObject();
                responseBody.put("message", errorMsg);
                responseBody.put("code", statusCode);
                response.setStatus(statusCode);
                sendResponse(response, responseBody);
            } catch (IOException e) {
                logger.error("发送错误响应失败", e);
            }
        }

        private void sendResponse(HttpServletResponse response, JSONObject responseBody) throws IOException {
            response.getWriter().println(JSON.toJSONString(responseBody));
        }
    }
}
